#!/usr/bin/env python3
# coding: utf-8
"""
2025-04-20 卡住查询
"""

from PySide6.QtCore import Slot, QSize, QTimer, Qt, QModelIndex, QMetaObject
from PySide6.QtGui import QAction, QKeySequence, QIntValidator, QTextCursor, QGuiApplication, QCloseEvent
from PySide6.QtWidgets import QMainWindow, QApplication, QVBoxLayout, QHBoxLayout, QPushButton, QWidget, QMessageBox, QLineEdit, QTableWidget, QTableWidgetItem, QHeaderView, QAbstractItemView, QLabel, QCheckBox, QTextEdit, QGroupBox

from top.wardyan.core.action.baseaction import WMainWindow, WQdialog
from top.wardyan.core.vo.vos import Record, listtorecords
from top.wardyan.core.widges.tables import WTableView

from top.wardyan.core.wdb.core.DBPool import POOL
from top.wardyan.core.wdb.core.db import query, find, exec
from config import Config
from top.wardyan.core.sqlite import sqliteconfigs

import threading
import time
import re
import sys
import json
import datetime

from apscheduler.schedulers.background import BackgroundScheduler
from top.wardyan.core.vo.mqtt import MqttMsg
from top.wardyan.core.mqtt import mqttconfig
from top.wardyan.core.erp.erp import ERP_CUSTCODE
from top.wardyan.core.utils.common import current_timestamp

import uuid

## 超过这个时间就自动强制kill掉 单位毫秒, 默认1小时
MAXCOST = Config.getconfig_int("autokill", "maxcost")
AUTOKILL = Config.getconfig_bool("autokill", "autokill")
AUTOREFRESH = Config.getconfig_int("autokill", "autorefresh")
if AUTOKILL is None : 
    AUTOKILL = False
if AUTOREFRESH is None : 
    AUTOREFRESH = 60
# maxcost_idle=3600000
# # 闲时段, 在闲时段的超时时长为maxcost_idle,多个小时用逗号隔开,24小时制
# idlehour=22,23,0,1,2,3,4,5,6
MAXCOST_IDLE = Config.getconfig_int("autokill", "maxcost_idle")
if MAXCOST_IDLE is None : 
    MAXCOST_IDLE = 3600000
_IDLEHOUR_ = Config.getconfig_str("autokill", "idlehour")
IDLEHOURS = []
if _IDLEHOUR_ is not None :
    ## IDLEHOURS = _IDLEHOUR_.split(",")
    for i in _IDLEHOUR_.split(",") :
        IDLEHOURS.append(int(i))

SQL_LISTALL = """
SELECT  
	MAX(CASE WHEN er.start_time < DATEADD(DAY, -1, GETDATE()) THEN 999999999 ELSE DATEDIFF(MILLISECOND, er.start_time, GETDATE()) END) AS 耗时毫秒
		, COUNT(sp.ecid) AS 线程数
		, MAX(er.percent_complete) AS 完成比例
		, er.session_id AS 线程ID
		, MAX(er.status)  AS 状态
		, CAST(MAX(er.wait_type) AS NVARCHAR(100)) AS 等待
		-- , OBJECT_NAME(qt.objectid) AS xObject			-- 对象
		, MAX(b.name) AS 名称
		, LEFT(MAX(SUBSTRING(qt.text, er.statement_start_offset / 2, ( CASE	WHEN er.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX), qt.text))* 2 ELSE er.statement_end_offset END  - er.statement_start_offset ) / 2)), 300) AS 正在执行语句
		, LEFT(MAX(qt.text), 300) AS  来源脚本
		, MAX(er.start_time) AS 开始时间
		, MAX(er.blocking_session_id )AS 被这个线程堵塞
		, MAX(er.wait_time) AS 等待时间
		, MAX(er.wait_resource) AS 等待类型
		, MAX(er.reads) AS 物理读行
		, MAX(er.writes) AS 写行
		,MAX(er.logical_reads) AS 逻辑度行
		, MAX(er.row_count) AS 行数
		, MAX(er.cpu_time) AS CPU耗时毫秒
--		 , dp.query_plan AS xQueryPlan		-- 执行计划 可能没有
		-- more
		, MAX(DB_NAME(sp.dbid)) AS 数据库
		, MAX(sp.loginame) AS 用户
		, MAX(er.command) AS 命令
		, MAX(sp.program_name) AS 程序
		, MAX(sp.hostname) AS 主机
FROM    sys.dm_exec_requests AS  er (NOLOCK)
INNER JOIN sys.sysprocesses AS  sp (NOLOCK) ON er.session_id = sp.spid
CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) AS qt
LEFT JOIN sys.objects AS b (NOLOCK) ON b.object_id = qt.objectid
WHERE   er.session_id > 50 -- Ignore system spids.
AND er.session_id <> @@SPID
GROUP BY er.session_id
ORDER BY MAX(CASE WHEN er.start_time < DATEADD(DAY, -1, GETDATE()) THEN 999999999 ELSE DATEDIFF(MILLISECOND, er.start_time, GETDATE()) END) desc
"""

QUERYRECORDS = None
        
class WWarningWindow (WQdialog) :
    def __init__(self) -> None:
        ##global cn
        super().__init__("sticklistwindow")
        self.table = None
        self.master = None
        self.clocking = True
        self.loginfos = []
        self.listtype = 0
        self.second = AUTOREFRESH
        self.data = None
        self.hour_checkboxes = []
        self.chkAutoKill = None
        self.txtDesc = None
        self.refreshbtn = None
        self.chkBlacklist = None
        self.txtMax = None
        self.txtMaxIdle = None
        self.lblMaxIdle = None
        self.lblMax = None
        self.lblDesc = None
        self.logstexts = None
        self.main()
        self.refreshTable()

        scheduler = BackgroundScheduler()
        scheduler.add_job(self.refreshhour, 'interval', seconds=5)
        scheduler.add_job(self.autorefreshtable, 'interval', seconds=1)
        # 移除直接调用mqtt心跳的定时任务，避免在非Qt线程中调用Qt功能
        # scheduler.add_job(mqttconfig.mqtt_heartbeat, 'interval', seconds=60)
        scheduler.start()

        ## 立刻发一个心跳
        # 在Qt主线程中发送心跳
        QTimer.singleShot(0, lambda: mqttconfig.mqtt_heartbeat())
        
    def torefreshTable(self) :
        QTimer.singleShot(0, lambda: self.refreshTable())

    def autorefreshtable(self) :
        global AUTOREFRESH
        global MAXCOST
        ## 不再由这里控制循环和休眠
        # while True :
        #     time.sleep(1)
        if self.clocking == False :
            if self.refreshbtn is not None:
                self.refreshbtn.setText("自动刷新已关闭")
            self.second = AUTOREFRESH
            if self.second <= 0 :
                self.second = 60
            return
        if self.refreshbtn is not None:
            self.refreshbtn.setText("自动刷新剩余倒计时: " + str(self.second)+"s")
        self.second -= 1
        if self.second <= 0 :
            self.refreshTable()
            self.second = AUTOREFRESH
            if self.second <= 0 :
                self.second = 60

            ## self是否包括属性data:
            if self.data is not None and len(self.data) > 0 :
                ##print(self.data)
                tosends = []
                for d in self.data :
                    cost = d.getInt("耗时毫秒")
                    id = d.getInt("线程ID")
                    objectName = d.getStr("名称")
                    ## print(cost, id)
                    auto_kill_checked = False
                    if self.chkAutoKill is not None:
                        auto_kill_checked = self.chkAutoKill.isChecked()
                    r = self.tokillsession( cost, id, MAXCOST, auto_kill_checked, objectName, d)
                    if r is not None :
                        tosends.append(r)
                ## 全部搞完之后立刻刷新一下
                ## self.refreshTable()
                ## QMetaObject.invokeMethod(self, b"refreshTable", Qt.ConnectionType.QueuedConnection)
                self.torefreshTable()
                if len(tosends) > 0 :
                    sss = json.dumps([r.tojsonstr() for r in tosends], ensure_ascii=False)
                    ## tosend转json

                    mqttmsg = MqttMsg(
                        id=str(uuid.uuid4()),
                        custcode=ERP_CUSTCODE,
                        timestamp=current_timestamp(),
                        topic=mqttconfig.TOPIC,
                        message=sss,
                        sender=ERP_CUSTCODE,
                        receiver=mqttconfig.TOPIC,
                        business=11101
                    )
                    mqttconfig.send(mqttconfig.TOPIC, mqttmsg.tojsonstr())
        else :
            ## 休眠1秒
            return

    def refreshhour(self) :
        currenthour = datetime.datetime.now().hour
        for a in self.hour_checkboxes :
            if currenthour == int(a.text()) :
                ## 设置背景颜色为绿色
                a.setStyleSheet("background-color: green;")
            else :
                a.setStyleSheet("")

    def tokillsession(self, cost : int, spid : int, maxcost : int, isToAutoKill : bool = False, objectName : str = "", record=None) :
        """ 
        TODO 收集记录数据并传到我们自己的服务器上
        """
        global QUERYRECORDS
        print("MAXCOST: " + str(maxcost) + "  cost: " + str(cost) + "  spid: " + str(spid) + " isToAutoKill: " + str(isToAutoKill) )
        # 显示当前时间戳,格式: 2023-07-05 09:08:06
        ok = False
        # if cost is not None and cost > maxcost and isToAutoKill:
        #     ok = True
        aaa : str = str(MAXCOST)
        if cost is not None and isToAutoKill :
            ## 如果现在是闲时, 就按闲时阈值,否则按 busy 阈值
            # 当前小时
            currenthour = datetime.datetime.now().hour
            idlehours = []
            for a in self.hour_checkboxes :
                ## 检查a是否勾选
                # if a.isChecked() :
                #     idlehours.append(int(a.text()))
                if a.isChecked() :
                    idlehours.append(int(a.text()))
            if currenthour in idlehours :
                ok = cost > MAXCOST_IDLE
                aaa = "闲时" + "(" + str(int(MAXCOST_IDLE / 1000 / 60 ))+ "分钟)"
                print("当前小时" + str(currenthour)+ ", 按闲时阈值")
            else :
                ok = cost > MAXCOST
                aaa = "忙时" + "(" + str(int(MAXCOST / 1000 / 60 ))+ "分钟)"
                print("当前小时" + str(currenthour)+ ",  按忙时阈值")
        if objectName is None or objectName == "" or objectName == "None":
            objectName = "" 
        s = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + " 已耗时毫秒:" + str(cost) + "(" + str(int(cost / 1000 / 60)) + "分钟)" + ",线程ID:" + str(spid) + ",对象名:" + objectName + "(需要KILL:" + str(ok)  + ",自动kill:" + str(isToAutoKill) + ",当前阈值:" + aaa + ")"
        #self.addlog(s)
        # 修复QMetaObject.invokeMethod调用方式
        QTimer.singleShot(0, lambda: self.addlog(s))
        if ok:
            #print("is to auto kill : kill session: " + str(spid))
            # exec("commit tran;  KILL " + str(spid) + "; begin tran;")
            # s = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "已KILL线程ID:" + str(spid) + ",对象名:" + objectName + "\n"
            # self.addlog(s)
            # 使用QTimer.singleShot在主线程中调用perform_kill方法
            # mqttmsg = MqttMsg(str(uuid.uuid4()), ERP_CUSTCODE, current_timestamp(), mqttconfig.TOPIC, record.tojsonstr(), ERP_CUSTCODE, mqttconfig.TOPIC, 11101)
            # mqttconfig.send(mqttconfig.TOPIC, mqttmsg)
            QUERYRECORDS = record
            QTimer.singleShot(0, lambda: self.perform_kill(spid, objectName))
            return record
        return None

    def perform_kill(self, spid: int, objectName: str):
        #print("is to auto kill : kill session: " + str(spid))
        global QUERYRECORDS
        ## 检查这个是否允许KILL
        #print("perform_kill", QUERYRECORDS)
        ok = False
        if self.chkBlacklist is not None and self.chkBlacklist.isChecked() :
            lists = sqliteconfigs.list_objectlist()
            if lists is not None and len(lists) > 0 :
                for a in lists :
                    if ok :
                        break
                    isre = int(a[2])
                    name = a[1]
                    if self.listtype == 2 :
                        if isre == 1 :
                            if re.match(name, objectName) :
                                print("黑名单模式, 正则符合,可以KILL")
                                QTimer.singleShot(0, lambda: self.addlog("黑名单模式, 正则符合,可以KILL:" + objectName))
                                ok = True
                        elif name == objectName :
                            print("黑名单模式, 直接匹配,可以KILL")
                            QTimer.singleShot(0, lambda: self.addlog("黑名单模式, 直接匹配,可以KILL:" + objectName))
                            ok = True
                        else :
                            print("黑名单模式, 不匹配,不能KILL,直接退出")
                            QTimer.singleShot(0, lambda: self.addlog("黑名单模式, 不匹配,不能KILL,直接退出:" + objectName))
                            ok = False
                    elif self.listtype == 1 :
                        if isre == 1 :
                            if re.match(name, objectName) :
                                print("白名单模式, 正则符合,不能KILL")
                                QTimer.singleShot(0, lambda: self.addlog("白名单模式, 正则符合,不能KILL:" + objectName))
                                ok = False
                        elif name == objectName :
                            print("白名单模式, 白名单直接匹配,不能KILL")
                            QTimer.singleShot(0, lambda: self.addlog("白名单模式, 白名单直接匹配,不能KILL:" + objectName))
                            ok = False
                        else :
                            print("白名单模式, 白名单不匹配,能KILL")
                            QTimer.singleShot(0, lambda: self.addlog("白名单模式, 白名单不匹配,能KILL:" + objectName))
                            ok = True
        else :
            print("未启用白名单或黑名单")
            QTimer.singleShot(0, lambda: self.addlog("未启用白名单或黑名单"))
            
            ok = True
        # TESTING    
        if ok :
            exec("commit tran;  KILL " + str(spid) + "; begin tran;")
            s = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "已KILL线程ID:" + str(spid) + ",对象名:" + objectName
            if QUERYRECORDS is not None :
                map = dict()
                map["xCost"]=QUERYRECORDS.getInt("耗时毫秒")
                map["xThreadCnt"]=QUERYRECORDS.getInt("线程数")
                map["xCompleteRate"]=QUERYRECORDS.getFloat("完成比例")
                map["xSessionID"]=QUERYRECORDS.getInt("线程ID")
                map["xStatus"]=QUERYRECORDS.getStr("状态")
                map["xWaitingType"]=QUERYRECORDS.getStr("等待")
                map["xName"]=QUERYRECORDS.getStr("名称")
                map["xSQL"]=QUERYRECORDS.getStr("正在执行语句")
                map["xSource"]=QUERYRECORDS.getStr("来源脚本")
                map["xStartTime"]=QUERYRECORDS.getStr("开始时间")
                map["xBlockingBy"]=QUERYRECORDS.getInt("被这个线程堵塞")
                map["xWaitingTime"]=QUERYRECORDS.getStr("等待时间")
                map["xPhysicalRead"]=QUERYRECORDS.getInt("物理读行")
                map["xWriteCnt"]=QUERYRECORDS.getInt("写行")
                map["xLogicalRead"]=QUERYRECORDS.getInt("逻辑度行")
                map["xRowCnt"]=QUERYRECORDS.getInt("行数")
                map["xCPUCost"]=QUERYRECORDS.getInt("CPU耗时毫秒")
                map["xDatabase"]=QUERYRECORDS.getStr("数据库")
                map["xUser"]=QUERYRECORDS.getStr("用户")
                map["xCmd"]=QUERYRECORDS.getStr("命令")
                map["xProgram"]=QUERYRECORDS.getStr("程序")
                map["xHost"]=QUERYRECORDS.getStr("主机")
                map["xDateTime"]=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]

                mqttmsg = MqttMsg(
                    id=str(uuid.uuid4()),
                    custcode=ERP_CUSTCODE,
                    timestamp=current_timestamp(),
                    topic=mqttconfig.TOPIC,
                    message=QUERYRECORDS.tojsonstr(),
                    sender=ERP_CUSTCODE,
                    receiver=mqttconfig.TOPIC,
                    business=11102
                )
                mqttconfig.send(mqttconfig.TOPIC, mqttmsg.tojsonstr())
                sqliteconfigs.save_killlog(map)

            self.addlog(s)
    
    def handle_double_click(self, index: QModelIndex) -> None:
        """
        处理单元格的双击事件。
        :param index: 被双击的单元格的模型索引。
        """
        pass

    def button_click_script(self) :
        global AUTOREFRESH
        self.clocking = not self.clocking
        self.refreshTable()
        if self.txtDesc is not None:
            AUTOREFRESH = int(self.txtDesc.text())
        self.clocking = not self.clocking
    def button_click_exit(self) :
        # 退出
        QApplication.quit()

    def button_click_clock(self) :
        self.clocking = not self.clocking

        
    def text_change(self) :
        global AUTOREFRESH
        if self.txtDesc is not None:
            AUTOREFRESH = int(self.txtDesc.text())
    def text_max_change(self) :
        global MAXCOST
        if self.txtMax is not None:
            MAXCOST = int(self.txtMax.text()) * 1000

    def text_max_idle_change(self) :
        global MAXCOST_IDLE
        if self.txtMaxIdle is not None:
            MAXCOST_IDLE = int(self.txtMaxIdle.text()) * 1000

    def main(self) :
        global MAXCOST
        global AUTOKILL
        global AUTOREFRESH
        global IDLEHOURS
        global MAXCOST_IDLE

        HEIGHT = 40
        layout = QVBoxLayout()
        layout.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignVCenter)
        #layout.setSpacing(10)
        #layout.addStretch()
        self.setLayout(layout)
        
        ## 按钮: 搜索 脚本清单 新增脚本 表/视图结构
        topwidget = QWidget()
        ##topwidget.setFixedHeight(100)
        layout.addWidget(topwidget)
        topwidget.setFixedHeight(50)
        
        # 设置背景颜色
        #topwidget.setStyleSheet("background-color: #ffff00;padding:0;margin:0;")  # 你可以将 #f0f0f0 替换为你想要的颜色代码

        toplayout = QHBoxLayout()
        toplayout.setAlignment(Qt.AlignmentFlag.AlignRight)
        ##self.setLayout(layout)
        ##layout.addWidget(toplayout)
        topwidget.setLayout(toplayout)
        
        ## 输入框,输入倒计时描述

        ## 勾选:自动杀掉超过1个小时的线程
        self.chkAutoKill = QCheckBox("自动杀掉超时线程")
        toplayout.addWidget(self.chkAutoKill)
        self.chkAutoKill.setFixedHeight(HEIGHT)
        self.chkAutoKill.setFixedWidth(120)
        if AUTOKILL :
            self.chkAutoKill.setChecked(True)
        else :
            self.chkAutoKill.setChecked(False)

        
        self.lblMaxIdle = QLabel("线程超时时长秒(闲时)")
        toplayout.addWidget(self.lblMaxIdle)
        self.lblMaxIdle.setFixedHeight(HEIGHT)
        self.lblMaxIdle.setFixedWidth(120)
        self.lblMaxIdle.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)

        self.txtMaxIdle = QLineEdit()
        self.txtMaxIdle.setFixedHeight(HEIGHT)
        self.txtMaxIdle.setFixedWidth(50)
        self.txtMaxIdle.setText(str(int(MAXCOST_IDLE / 1000)))
        toplayout.addWidget(self.txtMaxIdle)
        self.txtMaxIdle.textChanged.connect(self.text_max_idle_change)
        ## txtDesc只允许输入数字
        self.txtMaxIdle.setValidator(QIntValidator())

        
        self.lblMax = QLabel("线程超时时长秒(忙时)")
        toplayout.addWidget(self.lblMax)
        self.lblMax.setFixedHeight(HEIGHT)
        self.lblMax.setFixedWidth(120)
        self.lblMax.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)

        self.txtMax = QLineEdit()
        self.txtMax.setFixedHeight(HEIGHT)
        self.txtMax.setFixedWidth(50)
        self.txtMax.setText(str(int(MAXCOST / 1000)))
        toplayout.addWidget(self.txtMax)
        self.txtMax.textChanged.connect(self.text_max_change)
        ## txtDesc只允许输入数字
        self.txtMax.setValidator(QIntValidator())

        self.lblDesc = QLabel("自动刷新秒")
        toplayout.addWidget(self.lblDesc)
        self.lblDesc.setFixedHeight(HEIGHT)
        self.lblDesc.setFixedWidth(100)
        self.lblDesc.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)

        self.txtDesc = QLineEdit()
        self.txtDesc.setFixedHeight(HEIGHT)
        self.txtDesc.setFixedWidth(50)
        self.txtDesc.setText(str(AUTOREFRESH))
        toplayout.addWidget(self.txtDesc)
        self.txtDesc.textChanged.connect(self.text_change)
        ## txtDesc只允许输入数字
        self.txtDesc.setValidator(QIntValidator())

        btn = QPushButton("自动刷新剩余倒计时")
        btn.setFixedHeight(HEIGHT)
        btn.setFixedWidth(150)
        btn.clicked.connect(self.button_click_clock)
        toplayout.addWidget(btn)
        self.refreshbtn = btn

        
        btn = QPushButton("立刻刷新")
        btn.setFixedHeight(HEIGHT)
        btn.setFixedWidth(150)
        btn.clicked.connect(self.button_click_script)
        toplayout.addWidget(btn)
        ## 类型 0不启用, 1白名单, 2黑名单
        self.listtype = Config.getconfig_int("autokill","listtype")
        name = "不启用任何名单"
        if self.listtype is not None :
            if self.listtype == 1 :
                name = "启用白名单"
            elif self.listtype == 2 :
                name = "启用黑名单"
            else :
                name = "不启用任何名单"
        self.chkBlacklist = QCheckBox(name)
        toplayout.addWidget(self.chkBlacklist)
        self.chkBlacklist.setFixedHeight(HEIGHT)
        self.chkBlacklist.setFixedWidth(100)
        
        if self.listtype is not None :
            if self.listtype == 1 or self.listtype == 2 :
                self.chkBlacklist.setChecked(True)

        ### 
        btn = QPushButton("关闭")
        btn.setFixedHeight(HEIGHT)
        btn.setFixedWidth(150)
        btn.clicked.connect(self.button_click_exit)
        toplayout.addWidget(btn)

        ####################################################
        
        # ## 按钮: 搜索 脚本清单 新增脚本 表/视图结构
        # topwidget = QWidget()
        # topwidget.setFixedHeight(40)
        # layout.addWidget(topwidget)

        # toplayout = QHBoxLayout()
        # toplayout.setAlignment(Qt.AlignmentFlag.AlignRight)
        # ##self.setLayout(layout)
        # ##layout.addWidget(toplayout)
        # topwidget.setLayout(toplayout)
        
        #############
        ## 勾选框组, 标题是:闲时, 内容是0~23(每个小时一个勾选)
        
        # 创建一个QGroupBox，并设置其标题为"闲时"
        idle_group = QGroupBox("闲时")
        idle_layout = QHBoxLayout()  # 或者使用 QHBoxLayout()
        idle_group.setLayout(idle_layout)

        # 创建24个QCheckBox，分别对应0到23小时
        self.hour_checkboxes = []
        for hour in range(24):
            checkbox = QCheckBox(f"{hour}")
            idle_layout.addWidget(checkbox)
            self.hour_checkboxes.append(checkbox)
            #checkbox.checkState()
            if hour in IDLEHOURS :
                checkbox.setChecked(True)
            
        # 将QGroupBox添加到主布局中
        layout.addWidget(idle_group)

        # 设置背景颜色
        #topwidget.setStyleSheet("background-color: #ff0000;padding:0;margin:0;")  # 你可以将 #f0f0f0 替换为你想要的颜色代码

        ctrlw = QWidget()
        ctrllayout = QHBoxLayout()
        ctrlw.setLayout(ctrllayout)
        layout.addWidget(ctrlw)

        ctrllayout.setAlignment(Qt.AlignmentFlag.AlignRight)
        
        ctrlt = QWidget()
        tbllayout = QHBoxLayout()
        ctrlt.setLayout(tbllayout)
        layout.addWidget(ctrlt)

        self.table = WTableView(self, "sticklistwindow", "maintable", dbclick = self.handle_double_click)
        tbllayout.addWidget(self.table)

        ## 底部日志输出
        widgetlogs = QWidget()
        logslayout = QHBoxLayout()
        widgetlogs.setLayout(logslayout)
        widgetlogs.setFixedHeight(300)
        layout.addWidget(widgetlogs)

        logslayout.setAlignment(Qt.AlignmentFlag.AlignLeft)
        
        # widgetlogs = QWidget()
        # logsinnerlayout = QHBoxLayout()
        # widgetlogs.setLayout(logsinnerlayout)
        # logsinnerlayout.addWidget(ctrlt)

        # self.table = WTableView(self, "sticklistwindow", "maintable", dbclick = self.handle_double_click)
        # logsinnerlayout.addWidget(self.table)

        self.logstexts = QTextEdit()
        self.logstexts.setFixedHeight(290)
        #self.logstexts.setFixedWidth(500)
        self.logstexts.setReadOnly(True)
        logslayout.addWidget(self.logstexts)

    def addlog(self, text : str) :
        #print(self.loginfos)
        self.loginfos.append(text)
        if len(self.loginfos) > 1000 :
            # self.loginfos.pop(0)
            while len(self.loginfos) > 1000 :
                self.loginfos.pop(0)
        # 把self.loginfos里的内容转成字符串再写入self.logtexts
        if self.logstexts is not None:
            self.logstexts.setText("\n".join(self.loginfos))
        # 把self.loginfos写入self.logtexts
        # 自动滚动到最后一行
        if self.logstexts is not None:
            self.logstexts.moveCursor(QTextCursor.MoveOperation.End)

    def refreshTable(self) :

        data = []
        # r = Record()
        # r.set("name", "n1")
        # r.set("age", "2")
        # r.set("school", "??")
        # data.append(r)
        # data.append(r)
        # data.append(r)
        # data.append(r)
        # data.append(r)
        # ## self.renderTable(data)
        # 查看正在执行的语句
        # TODO
        rs = query(SQL_LISTALL, ())
        if rs is None :
            rs = []
        data = listtorecords(rs)
        ## print(rs)
        self.data = data
        for d in data :
            # 把数据封装到一个dict中,传给save_databasewatch
            ## 超过1分钟的才需要记录
            if d.getInt("耗时毫秒") > 60000 :
                map = dict()
                map["xCost"]=d.getInt("耗时毫秒")
                map["xThreadCnt"]=d.getInt("线程数")
                map["xCompleteRate"]=d.getFloat("完成比例")
                map["xSessionID"]=d.getInt("线程ID")
                map["xStatus"]=d.getStr("状态")
                map["xWaitingType"]=d.getStr("等待")
                map["xName"]=d.getStr("名称")
                map["xSQL"]=d.getStr("正在执行语句")
                map["xSource"]=d.getStr("来源脚本")
                map["xStartTime"]=d.getStr("开始时间")
                map["xBlockingBy"]=d.getInt("被这个线程堵塞")
                map["xWaitingTime"]=d.getStr("等待时间")
                map["xPhysicalRead"]=d.getInt("物理读行")
                map["xWriteCnt"]=d.getInt("写行")
                map["xLogicalRead"]=d.getInt("逻辑度行")
                map["xRowCnt"]=d.getInt("行数")
                map["xCPUCost"]=d.getInt("CPU耗时毫秒")
                map["xDatabase"]=d.getStr("数据库")
                map["xUser"]=d.getStr("用户")
                map["xCmd"]=d.getStr("命令")
                map["xProgram"]=d.getStr("程序")
                map["xHost"]=d.getStr("主机")
                sqliteconfigs.save_databasewatch(map)
                
        if self.table is not None:
            self.table.render(data)